package jmind.core.ibatis;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.sql.DataSource;

import jmind.core.lang.IProperties;
import jmind.core.lang.SourceProperties;
import jmind.core.util.DataUtil;

import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 配置文件的写法 ${}_ibatis.xml
 * @author youfang.mu
 * @2014年4月2日 上午11:11:37
 */
public class SqlSessionFactoryManager extends AbstractDataSource{
	private static final Logger LOG = LoggerFactory.getLogger(SqlSessionFactoryManager.class);

    // each business component has it's own individual ibatis config file, e.g feed_ibatis.xml
    private static final String IBATIS_CONFIG_SUFFIX = "_ibatis.xml";

    private static final Object LOCK = new Object();

    // cache to store tha instanced objects for sql session factories
    private final Map<String, SqlSessionFactory> sqlSessionFactoryMap = new ConcurrentHashMap<String, SqlSessionFactory>();
    /**应用的名字  用来找ibatis.xml 文件*/
    private String componentName;
    private String environment; 
    /**isRead TRUE slave | false master*/
    private boolean isRead = Boolean.FALSE; 
    private boolean isAutoCommit = Boolean.TRUE;
    
    /**
     * ibatis 数据库拆分
     * @param ibatisXml 格式 user_ibatis.xml 传递user就可以了
     * @param dbEnv 连接的数据库
     */
    public SqlSessionFactoryManager(String ibatisXml, String dbEnv) {
    	this.componentName = ibatisXml;
    	this.environment = dbEnv;
    	this.isRead = Boolean.FALSE; 
    	this.isAutoCommit = Boolean.TRUE;
    }
    
    public void isRead(boolean isRead) {
    	this.isRead = isRead; 
    }
    
    public void isAutoCommit(boolean isAutoCommit) {
    	this.isAutoCommit = isAutoCommit;
    }

    public SqlSession getSqlSession() {
    	try {
			SqlSessionFactory sqlSessionFactory = this.getSqlSessionFactory();
			long start = System.currentTimeMillis();
			SqlSession session = sqlSessionFactory.openSession(isAutoCommit);
			long end = System.currentTimeMillis();
			if (end - start > 100) {
			    LOG.warn("open sql session:" + environment + " " + (end - start) + " millionSeconds");
			}
			return session;
    	} catch (Exception e) {
			LOG.error("get getInstanceSqlSession error:" + e.getMessage());
			return null;
		}
    }
    
    public SqlSession getBatchSqlSession() {
    	try {
	        SqlSessionFactory sqlSessionFactory = this.getSqlSessionFactory();
	        long start = System.currentTimeMillis();
	        SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
	        long end = System.currentTimeMillis();
	        if (end - start > 100) {
	            LOG.warn("open sql session:" + environment + " " + (end - start) + " millionSeconds");
	        }
	        return session;
    	} catch (Exception e) {
			LOG.error("get getInstanceSqlSession error:" + e.getMessage());
			return null;
		}
    }
    
    private SqlSessionFactory getSqlSessionFactory() {

        Map<String, SqlSessionFactory> tempSqlSessionFactoryMap = sqlSessionFactoryMap;

        String environmentKey = componentName + "::" + environment;

        if (!tempSqlSessionFactoryMap.containsKey(environmentKey)) {
            synchronized (LOCK) {
                if (!tempSqlSessionFactoryMap.containsKey(environmentKey)) {
                    try {
                    	System.out.println("......getSqlSessionFactory: componentName="+componentName+" environment="+environment);
                        if(DataUtil.isEmpty(componentName) || DataUtil.isEmpty(environment)){
                        	throw new Exception("参数为空:componentName="+componentName+" environment="+environment);
                        }
                    	LOG.info(String.format("build datasource:%s", environmentKey));
                        long start = System.currentTimeMillis();

                        SqlSessionFactory factory = buildSqlSessionFactory();

                        tempSqlSessionFactoryMap.put(environmentKey, factory);

                        long end = System.currentTimeMillis();
                        if (end - start > 1000) {
                            LOG.info(String.format("build datasource %s cost %d ms", environmentKey, (end - start)));
                        }
                        return factory;
                    } catch (Exception e) {
                    	System.out.println(e);
                        LOG.error("build environment error environment:" + environmentKey +" componentName:"+ componentName+ " with error " + e.getMessage());
                        return null;
                    } 
                }
            }
        }
        return tempSqlSessionFactoryMap.get(environmentKey);
    }
    
    /**
     * @param environment zk 配置
     * @param ibatisConfigFile ibatis 配置
     * @param isRead TRUE slave | false master
     * @return
     * @throws java.io.IOException
     */
    private SqlSessionFactory buildSqlSessionFactory() throws IOException{
    	SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
    	if(isRead){
    		sessionFactoryBean.setUseTransactionAwareDataSource(false);
    	}
    	sessionFactoryBean.setEnvironmentId(environment);
    	DataSource ds = getResource(environment);
    	sessionFactoryBean.setDataSource(ds);
    	String ibatisConfigFile = componentName + IBATIS_CONFIG_SUFFIX;
    	return sessionFactoryBean.buildSqlSessionFactory(ibatisConfigFile);
    }

	@Override
	public IProperties getProperties() {
		return SourceProperties.getDataSource();
	}
}

